package com.yuw.reflectiondemo;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestDriver {

    /**
     * Method工具类
     */
    @Test
    public void test05() {
        // 2 通过实例对象.getClass方法获取
        Student student = new Student();
        Student student1 = new Student("张三", 20, "10086", "男");
        Class clazz2 = student.getClass();

        try {
            // 获取Method对象
            // 如果有重载的话，通过参数列表来区分

            Method method = clazz2.getDeclaredMethod("doStuding");
            // 第一个参数是 方法名；
            // 第二个参数是 从在方法的参数列表；参数列表需要使用 Class 来表示
            Method method1 = clazz2.getDeclaredMethod("doStuding", String.class);

            // 使用反射机制的 invoke 途径，执行该方法对象
            method1.invoke(student1, "nuc");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 反射操作
     * 可以借助于第三方的工具： BeanUtils
     */
    @Test
    public void test04() {
        // 2 通过实例对象.getClass方法获取
        Student student = new Student();
        Student student1 = new Student("张三", 20, "10086", "男");
        Class clazz2 = student.getClass();
        try {
            System.out.println("反射之前：" + student1);
            // 获取属性的描述类Field
            Field fieldStuName = clazz2.getDeclaredField("stuName");
            // 设置反射机制可以操作私有类型
            fieldStuName.setAccessible(true);

            // 采用反射机制，动态操作Field对象
            // 第一个参数是反射操作时需要附加的实例对象；
            // 第二个参数是该属性的设置的值
            fieldStuName.set(student1, "李四");

            System.out.println("反射之后：" + student1);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过反射机制，使用Constructor获取被描述类的实例对象
     * <p>
     * Java中获取类的实例对象的方式：
     * 1、	new
     * 2、	对象流
     * 3、	反射（通过Class.newInstance   constructor.newInstance）
     * 4、	深克隆（深复制）
     */
    @Test
    public void test03() {
        // 2 通过实例对象.getClass方法获取
        Student student = new Student();
        Class clazz2 = student.getClass();

        try {
            // 通过Class获取被描述类的构造方法的描述类Constructor
            Constructor<Student> constructor = clazz2.getDeclaredConstructor();
            Constructor<Student> constructor2 = clazz2.getDeclaredConstructor();
            Constructor<Student> constructor3 = clazz2.getDeclaredConstructor(String.class);
            Constructor<Student> constructor4 =
                    clazz2.getDeclaredConstructor(String.class, int.class, String.class, String.class);

            // 获取Constructor对象不是单例模式
            System.out.println("constructor == constructor2:" + (constructor == constructor2));

            // 使用Constructor获取类的实力对象
            Student student1 = constructor.newInstance();
            // 使用newInstance方法是，需要和获取时给定的参数列表保持一致
            Student student4 = constructor4.newInstance("张三", 20, "10086", "男");

            System.out.println("student4:" + student4);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过反射机制，使用Class获取被描述类的实例对象
     */
    @Test
    public void test02() {
        // 1 类名.Class获取
        Class<Student> clazz = Student.class;

        try {
            // 使用Class工具类的 newInstance方法获取被描述类的实例对象  等价于 new 操作
            Student student = clazz.newInstance();

            Student student1 = clazz.newInstance();

            System.out.println("student1 == student:" + (student1 == student));
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * Class反射工具类获取的途径
     * 结论：不管通过什么途径，一个类只有唯一的一个Class描述类；
     * Class是一个单例模式；
     * 反射编程，一般都是从获取一个类的Class类开始；
     */
    @Test
    public void test01() {
        // 1 类名.Class获取
        Class clazz = Student.class;

        // 2 通过实例对象.getClass方法获取
        Student student = new Student();
        Class clazz2 = student.getClass();

        // 3 使用Class类的静态方法 forName获取一个给定的完整包路径名类的Class
        Class clazz3 = null;
        try {
            clazz3 = Class.forName("com.yuw.reflectiondemo.Student"); // 完整包路径名
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println("clazz == clazz2:" + (clazz == clazz2));
        System.out.println("clazz == clazz3:" + (clazz == clazz3));
    }

}
